4.06. Неиспользуемый код и технический долг
Неиспользуемый код и технический долг
Мёртвый код: переменные, методы, классы, импорты
Мёртвый код — фрагменты программы, которые никогда не выполняются в процессе работы приложения.
Типы мёртвого кода:
| Тип | Пример | Последствия |
|---|---|---|
| Недостижимый код | Код после return или в ветке if (false) | Загромождает кодовую базу |
| Неиспользуемые переменные | Объявленные, но не читаемые переменные | Вводит в заблуждение при чтении кода |
| Неиспользуемые методы | Методы без вызовов | Увеличивают размер сборки |
| Неиспользуемые классы | Классы без ссылок | Усложняют навигацию по коду |
| Неиспользуемые импорты | Подключённые, но не используемые модули | Замедляют сборку и загрузку |
Пример мёртвого кода:
public class OrderService
{
private readonly ILogger _logger;
private readonly int _unusedField = 42; // Неиспользуемое поле
public OrderService(ILogger logger, IEmailService emailService)
{
_logger = logger;
// emailService не используется — неиспользуемый параметр
}
public void ProcessOrder(Order order)
{
ValidateOrder(order);
return; // Ранний возврат
// Мёртвый код — никогда не выполнится
SaveOrder(order);
SendConfirmation(order);
}
private void UnusedMethod()
{
// Метод без вызовов
}
}
Как его находить: статический анализ, coverage
Статический анализ — проверка кода без его выполнения для обнаружения потенциальных проблем.
Инструменты статического анализа:
| Язык | Инструменты |
|---|---|
| C# | Roslyn Analyzers, ReSharper, SonarQube |
| Java | SonarQube, PMD, SpotBugs |
| Python | pylint, flake8, vulture |
| JavaScript | ESLint, SonarJS |
| Go | go vet, staticcheck |
Пример конфигурации ESLint для обнаружения неиспользуемого кода:
{
"rules": {
"no-unused-vars": "error",
"no-unreachable": "error",
"no-unused-expressions": "error",
"no-unused-private-class-members": "error"
}
}
Покрытие кода (code coverage) — метрика, показывающая долю кода, выполненного при тестировании.
Инструменты измерения покрытия:
| Платформа | Инструмент |
|---|---|
| .NET | coverlet, dotCover |
| Java | JaCoCo, Cobertura |
| Python | coverage.py |
| JavaScript | Istanbul/nyc |
Пример анализа покрытия в Python:
# Запуск тестов с измерением покрытия
coverage run -m pytest
coverage report --show-missing
coverage html # Генерация HTML-отчёта
Пример отчёта покрытия:
Name Stmts Miss Cover Missing
----------------------------------------------
order_service 50 5 90% 23-25, 42, 47
payment_gateway 30 15 50% 12-18, 25-32
Строки 23-25, 42, 47 в order_service и большая часть payment_gateway не покрыты тестами — возможный мёртвый код или недостаточное тестирование.
Последствия: усложнение поддержки, рост времени сборки
Последствия мёртвого кода:
-
Усложнение понимания кода Разработчики тратят время на анализ кода, который не влияет на поведение системы.
-
Рост размера сборки Каждый неиспользуемый метод и класс увеличивает размер итогового артефакта.
-
Замедление сборки Компилятор обрабатывает весь код, включая мёртвые участки.
-
Ложные срабатывания при рефакторинге Инструменты рефакторинга могут предлагать изменения в мёртвом коде.
-
Риск случайной активации Мёртвый код может быть случайно задействован при изменении логики.
Пример влияния на размер сборки:
# С мёртвым кодом
-rw-r--r-- 1 user staff 15M app-with-dead-code.dll
# Без мёртвого кода
-rw-r--r-- 1 user staff 12M app-clean.dll
# Экономия: 3 МБ (20%)
Удаление и комментирование
Комментирование мёртвого кода — практика закомментировать, а не удалить неиспользуемый код.
Пример плохой практики:
public void ProcessOrder(Order order)
{
ValidateOrder(order);
// SaveOrder(order); // Закомментировано 2023-05-15, не нужно временно
// SendConfirmation(order); // Закомментировано 2023-05-15
// LogToLegacySystem(order); // Старая система, убрать позже
NotifyCustomer(order);
}
Проблемы закомментированного кода:
-
Загрязнение кодовой базы Комментарии с кодом занимают место и отвлекают внимание.
-
Неопределённость срока хранения "Временно" превращается в "навсегда". Через год никто не помнит, зачем код был закомментирован.
-
Отсутствие версионного контроля Закомментированный код дублирует функциональность системы контроля версий (Git).
-
Сложность поддержки При изменении сигнатуры методов закомментированный код становится некорректным, но остаётся в файле.
Правильный подход — удаление с сохранением в истории версий:
# Удаляем мёртвый код
git rm dead-code.cs
# Фиксируем изменение с описанием
git commit -m "Удалён неиспользуемый класс LegacyProcessor"
# При необходимости код можно восстановить из истории
git log --all --full-history -- "**/LegacyProcessor.cs"
git checkout <commit-hash> -- path/to/LegacyProcessor.cs
Когда допустимо оставлять закомментированный код
Закомментированный код допустим в редких случаях:
- временные отладочные фрагменты в процессе разработки (до коммита)
- альтернативные реализации с явным указанием причины выбора
- документирование намерений через примеры использования
Во всех случаях требуется подробный комментарий с датой и причиной.